
/* The program is to test stand alone AC-4
 * on randomly generated problems. 5 Feb 2004
 *
 * Test: ac4tesst.cpp
 * AC-4: ac4.cpp, ac4.h
 * CSP representation: csp.cpp, csp.h 
 * problem generator: urbcsp.cc
 * compiler: gcc -pg 
 *
 * this case needs long time to solve
 * a.out 50 50 150 1850 99 2
 */

#include "mac3all-k-residue.h" // for ALGORITHM
#include "backtracking.h"
#include <errno.h>
// #include <cstdio>

#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream> // used to tranform values to characters 
#include <string>

#include "resourcesUsageCounter.h"

// #ifdef USEAC3kR
// for k-residue
BT *Residues::csp;
// #endif

// the parameters of a CSP: n # of var's; d size of domain; and 
// e the number of constraints

// used for the statistics
int num_consistent_instances[50]; 
int num_nonconsistent_instances[50]; 
int ncip = 0, nnip = 0;

// void consistent_instances(int, int, int);
// void nonconsistent_instances(int, int, int);

// to compute the average numbers
double average(double averageValue, int numOfValues, double newValue);
void random(int argc,char *argv[]);
void table(int argc,char *argv[]);
void batchTable(int argc, char *argv[]);
void domino(int argc,char *argv[]);

#ifdef USEAC3kR
#define METHODS 15
char *methods[15] = {"10", "11", "12", "30", "31", "40", "41",
		     "50", "51", "60", "61", "70", "71", "80", "81"};
// #define CACHE_SIZES 7 
// char *sizes[CACHE_SIZES] = {"1", "2", "3", "4", "6", "8", "10"}; 

// for real problem with 2, 4, 6, 8 cachesize
#define CACHE_SIZES 3
char *sizes[CACHE_SIZES] = {"5", "7", "9"};

#endif

int main(int argc, char* argv[])
{
  if(argc <2)
  {
	  printf("System needs at least 1 input\n");
	  return 1;
  }
  if(argv[1][0]!='-')
  {
	  printf("First argument must be function switcher\n");
	  return 1;
  }
  switch(argv[1][1])
  {
  case 't':
	  table(argc,argv);
	  break;
  case 'r':
	  random(argc,argv);
	  break;
  case 'd':
	  domino(argc,argv);
	  break;
  case 'b': 
          batchTable(argc, argv);
          break;
#ifdef USEAC3kR
  case 'a':
    {
      if (argc != 3) {
	cout << "usage -a fileOfProblemSets" << endl;
	cout << "-a: to test all combination of cacheSize and policies" << endl;
	exit (-1); 
      }
      
      char *args[5] = {argv[0], argv[1], argv[2]};
      /* for get data for random1 cases 
      args[3] = sizes[3];
      for (int i = 10; i < METHODS; i++) {
	args[4] = methods[i];
	batchTable(argc+2, args);
      }
    
      for(int j = 4; j < CACHE_SIZES; j++){ 
	args[3] = sizes[j];
	for (int i = 0; i < METHODS; i++) {
	  args[4] = methods[i];
	  batchTable(argc+2, args);
	}
      }
      */

      for(int j = 0; j < CACHE_SIZES; j++){ 
	args[3] = sizes[j];
	for (int i = 0; i < METHODS; i++) {
	  args[4] = methods[i];
	  batchTable(argc+2, args);
	}
      }
    }
    break;
#endif
  default:
	  printf("Switch not recognized\n");
  }
  return 0;
}


void table(int argc,char *argv[])
{
  if (argc != 3)
    {
      printf("usage: filename\n");
      return;
    }
  
  ResourcesUsageCounter clock;


  cout << "Using table found in file " << argv[2] << endl;
  cout << "\nInstance " << 0 << ":\n";
  // the random instances    
  BT csp(argv[2]);
  //csp.printCSP();
  clock.start();
  if(csp.solve()){
    // printf("Instance %d is now arc consistent.\n", i);
    csp.checkSolution();
    //csp.printValueSelection();
  }
  else{
    // printf("Instance %d is not satisfied\n", i);
    // total_checks_nonconsistent += csp.checks();
    nnip++;
	cout << "CSP inconsistent, no answer set checking." << endl;
  }
  clock.stop();
  csp.statistics();
  cout << ':' << "Time used:" << clock << '\n';
  return;
}

void domino(int argc,char *argv[])
{
  int N, D, I;
  int n, d, e, i;
  if (argc != 5)
    {
      printf("usage: #vars #vals instances\n");
      return;
    }
     
  N = n = atoi(argv[2]);
  D = d = atoi(argv[3]);
  e = n;
  I = atoi(argv[4]);

  double averageCChecks = 0;
  double averageDChecks = 0;
  // for k-residue
  double averageOChecks = 0;

  double averageBacktracks = 0;
  double averageMaxSearchDepth = 0;
  
  ResourcesUsageCounter clock, clock1;


  cout << "The parameters of the domino instances\n"
       << "  (n=" << n 	 
       << ", d="  << d << ", e=" << e << ", # of instances=" << I << ") \n";
  // cout << "#initial removed value:totally removed values:arc consistent(1)\n";
  for (i=0; i<I; ++i){

    cout << "\nInstance " << i << ":\n";

    // the random instances    
    BT csp(n, d);
    clock.start();

    clock1.reset();
    clock1.start();
    if(csp.solve()){
      // printf("Instance %d is now arc consistent.\n", i);
      num_consistent_instances[ncip] = i;
      // total_checks_consistent += csp.checks();
      ncip++;
	  csp.checkSolution();
	  //csp.printValueSelection();
    }
    else{
      // printf("Instance %d is not satisfied\n", i);
      num_nonconsistent_instances[nnip] = i;
      // total_checks_nonconsistent += csp.checks();
      nnip++;
	  cout << "CSP inconsistent, no answer set checking." << endl;
    }
    
    clock1.stop();
    clock.stop();
    csp.statistics();
    cout << ':' << "Time used:" << clock1 << '\n';
    averageCChecks = average(averageCChecks, i, csp.cchecks());
    averageDChecks = average(averageDChecks, i, csp.dchecks());
    // for k-residue
    averageOChecks = average(averageOChecks, i, csp.ochecks());
    averageBacktracks = average(averageBacktracks, i, 
				csp.getNumOfBacktracks());
    averageMaxSearchDepth = average(averageMaxSearchDepth, i, 
				    csp.getMaxSearchDepth());
  }

  std::cout << "average cchecks+dchecks+ochecks: average backtracks: average search depth\n";
  std::cout << averageCChecks << '+' << averageDChecks << '+' 
	    << averageOChecks
	    << ':' << averageBacktracks << ':' 
	    << averageMaxSearchDepth << '\n'; 
    printf("the consistent instances are (%d) \n", ncip);
    for(i=0; i<ncip; i++) printf(" %d", num_consistent_instances[i]);
    printf("\n");
    printf("the inconsistent instances are (%d) \n", nnip);
    for(i=0; i<nnip; i++) printf(" %d", num_nonconsistent_instances[i]);
    printf("\n");
    std::cout << "Total time:" << clock << '\n';
  return;
}



void random(int argc,char *argv[])
{
  int N, D, C, T, I;
  int n, d, e,    i;
  int S, OS;

#ifdef USEAC3kR
  if (argc != 10)
    {
      printf("usage: #vars #vals #constraints #nogoods seed instances residueCacheSize residueMethod\n");
      return;
    }
#else
  if (argc != 8)
    {
      printf("usage: #vars #vals #constraints #nogoods seed instances\n");
      return;
    }
#endif

     
  n = N = atoi(argv[2]);
  d = D = atoi(argv[3]);
  e = C = atoi(argv[4]);
  T = atoi(argv[5]);
  OS = S = atoi(argv[6]);
  I = atoi(argv[7]);
  if (S > 0){
    S = -S;
    OS = -OS;
  } 

#ifdef USEAC3kR
  int cacheSize = atoi(argv[8]);
  int method = atoi(argv[9]);
#endif

  double averageCChecks = 0;
  double averageDChecks = 0;

  // for k-residue
  double averageOChecks = 0;

  double averageBacktracks = 0;
  double averageMaxSearchDepth = 0;
  
  ResourcesUsageCounter clock, clock1;


  cout << "The parameters of the random instances\n"
       << "  (n=" << n 	 
       << ", d="  << d << ", e=" << e << " , disallowed=" << T
       << ", # of instances=" << I << ", seed=" << -S << ") \n";
  // cout << "#initial removed value:totally removed values:arc consistent(1)\n";
  for (i=0; i<I; ++i){

	cout << "\nInstance " << i << ":" << endl;

    // the random instances    
    BT csp(n, d, e, T, &S);
#ifdef USEAC3kR
    csp.configureResidue(cacheSize, method);
#endif

    // time(&st);
     //csp.printVG();
    // if( i < I-1) continue; 
    clock.start();

    clock1.reset();
    clock1.start();
	if(csp.solve()){
      // printf("Instance %d is now arc consistent.\n", i);
      num_consistent_instances[ncip] = i;
      // total_checks_consistent += csp.checks();
      ncip++;
	  csp.checkSolution();
	  //csp.printValueSelection();
    }
    else{
      // printf("Instance %d is not satisfied\n", i);
      num_nonconsistent_instances[nnip] = i;
      // total_checks_nonconsistent += csp.checks();
      nnip++;
	  cout << "CSP inconsistent, no answer set checking." << endl;
    }
    
    clock1.stop();
    clock.stop();
    csp.statistics();
    cout << ':' << "Time used:" << clock1 << '\n';
    averageCChecks = average(averageCChecks, i, csp.cchecks());
    averageDChecks = average(averageDChecks, i, csp.dchecks());
    // for k-residue
    averageOChecks = average(averageOChecks, i, csp.ochecks());
    averageBacktracks = average(averageBacktracks, i, 
				csp.getNumOfBacktracks());
    averageMaxSearchDepth = average(averageMaxSearchDepth, i, 
				    csp.getMaxSearchDepth());
	//cout << "Finished with Instance " << i << endl;
	//getchar();
    // time(&et);
    // total_checks += num_checks;
    // printf("timing from %ld to %ld\n", st, et);
  }
/*
  if (ncip != 0){ 
    total_checks_consistent /= ncip; 
    consistent_instances(I, T, OS);
  }
  else total_checks_consistent = 0;
  if (nnip != 0){
    total_checks_nonconsistent /= nnip;
    nonconsistent_instances(I, T, OS);
  }
  else total_checks_nonconsistent = 0;
*/

  std::cout << "average cchecks+dchecks+ochecks: average backtracks: average search depth\n";
  std::cout << averageCChecks << '+' << averageDChecks
	    << '+' << averageOChecks
	    << ':' << averageBacktracks << ':' 
	    << averageMaxSearchDepth << '\n'; 
    printf("the consistent instances are (%d) \n", ncip);
    for(i=0; i<ncip; i++) printf(" %d", num_consistent_instances[i]);
    printf("\n");
    printf("the inconsistent instances are (%d) \n", nnip);
    for(i=0; i<nnip; i++) printf(" %d", num_nonconsistent_instances[i]);
    printf("\n");
    //  printf("the average checks for nonconsistent is %d (%d) \n", total_checks_nonconsistent, nnip);
    std::cout << "Total time:" << clock << '\n';
  return;
}

/**
 * ouput: output the formated statistics of solving a (or some) problems.
 * for k-residue algorithms
 */

void output(ofstream& results, 
	    string fName, int cSize, int method, ResourcesUsageCounter& clock, 
	    double ccks, double dcks, double ocks,
	    int sat, int unSat)
{
  results << fName << ',' << cSize << ',' << method << ','
	  << clock.getUserT() << ',' 
	  << clock.getSystemT() << ','
	  << clock.getTotalT() << ','
	  << ccks << ',' << dcks << ',' << ocks << ','
	  << sat << ',' << unSat << endl;
}

/**
 * ouput: output the formated statistics of solving a (or some) problems.
 * for other algorithms
 */

void output(ofstream& results, 
	    string fName, const string& algorithm, ResourcesUsageCounter& clock, 
	    double ccks, double dcks, double ocks,
	    int sat, int unSat)
{
  results << fName << ',' << algorithm << ','
	  << clock.getUserT() << ',' 
	  << clock.getSystemT() << ','
	  << clock.getTotalT() << ','
	  << ccks << ',' << dcks << ',' << ocks << ','
	  << sat << ',' << unSat << endl;
}

/**
 * batchTable: produce the data according to the instructions in 
 * the file of (e.g., experiment.conf). The instances in experiment.conf 
 * are in table format. 
 */

void batchTable(int argc, char *argv[])
// int main(int argc, char *argv[])
{

#ifdef USEAC3kR
  if (argc != 5) {
    cerr << "usage: -b problemSetsFile residueCacheSize residueMethod" << endl;
    exit(-1);
  }
  int cacheSize = atoi(argv[3]);
  int method = atoi(argv[4]);
#else
  if(argc !=3)
    {
      cerr << "usage: -b problemSetsFile" << endl;
      exit(-1);
    }
#endif

  string problemSets(argv[2]);

  string consumeNL;

  // generate the filename for output. 
  string rootName(argv[2]), outputName;
  size_t pos; 
  
  pos = rootName.find('.');
  if (pos != string::npos)
    outputName = rootName.substr(0, pos);
  else outputName = rootName;
  outputName += ".results.txt";

  // cout << "Input name is: " << rootName << endl
  //     << "Output name is: " << outputName << endl;

  // exit(-1);

  ifstream configure(problemSets.c_str());
  ofstream results(outputName.c_str(), ios::app); 

  if(!configure || !results) {
    cerr << "The configuration experiment.conf does not exist," << endl;
    cerr << "or can not open the output data file." << endl;
    exit (-1);
  }
#ifdef USEAC3kR
  results << "Problem Name, Cache Size, Method, "
	  << "User Time(s), System Time(s), Total Time(s), "
	  << "Constraint checks, Domain Checks, Other Checks, "
	  << "Satisfiable Instances, Non-satisfiable Instances"
	  << endl;
#else
  results << "Problem Name, " << ALGORITHM << ", "
	  << "User Time(s), System Time(s), Total Time(s), "
	  << "Constraint checks, Domain Checks, Other Checks, "
	  << "Satisfiable Instances, Non-satisfiable Instances"
	  << endl;
#endif

  string directory;

  // get the directory of the experimental files
  while (! configure.eof()){
    configure >> directory;
    // skip the comments and empty lines
    if (directory.empty() || directory[0] == '%'){
      getline(configure, consumeNL); 
      continue;
    }
    else break;
  }

  // get the directory
  
  if (directory.empty() || directory != "directory"){
    cerr << "directory should be the first effective line in experiment.conf" << endl;
    exit (-1);
  }
  else {
    configure >> directory; // get the directory
    getline(configure, consumeNL);
  }
 
  while(!configure.eof()){
    string f1, f2;
    int start, end;
    int averagePerformance;

    configure >> f1;

    // skip comments and empty lines
    if (f1.empty() || f1[0] == '%'){
      getline(configure, consumeNL); 
      continue;
    }

    configure >> f2;
    configure >> averagePerformance; 
    configure >> start;
    configure >> end;
 
    // skip the rest of the line
    getline(configure, consumeNL);

    cout << "file:" << f1 << '?' << f2 << endl;
    cout << "start:" << start << ' '
	 << "end:" << end << endl;
     
    // average statistics for problems from start to end
    double averageCChecks = 0;
    double averageDChecks = 0;
    // for k-residue
    double averageOChecks = 0;
    double averageBacktracks = 0;
    double averageMaxSearchDepth = 0;
    // clock: time used from start to end
    // clocks: time used for one instance. 
    ResourcesUsageCounter clock, clock1;
    ncip = 0; 
    nnip = 0;
    
    // i is used to compute the average statistics
    for(int instance = start, i = 0; instance <= end; instance++) {
      string fileName;
      ostringstream str;
      
      str << instance; 
      fileName = directory + f1 + str.str() + f2;
 
      // cout << "file:" << fileName << endl;
      // getline(cin, consumeNL);

      ifstream instanceFile;
      instanceFile.open(fileName.c_str());
      if (! instanceFile) {
        cerr << "The file " << fileName << "does not exist." << endl;
        continue; // process the next file
      }
      /*
	1. BT csp(instanceFile) -- file object or name? 
	2. csp.solve()
	3. if (averagePerformance) {
	4.    average the performance
	5  }
	6. else output the performance 
      */

      BT csp(fileName.c_str());
#ifdef USEAC3kR
      csp.configureResidue(cacheSize, method);
#endif

      clock.start();

      clock1.reset();
      clock1.start();
      if(csp.solve()){
	ncip++;
	csp.checkSolution();
	//csp.printValueSelection();
      }
      else{
	nnip++;
	cout << "CSP inconsistent, no answer set checking." << endl;
      }
    
      clock1.stop();
      clock.stop();
      csp.statistics();
      cout << ':' << "Time used:" << clock1 << endl;
      
      if(averagePerformance) {
	averageCChecks = average(averageCChecks, i, csp.cchecks());
	averageDChecks = average(averageDChecks, i, csp.dchecks());
      // for k-residue
	averageOChecks = average(averageOChecks, i, csp.ochecks());
	averageBacktracks = average(averageBacktracks, i, 
				  csp.getNumOfBacktracks());
	averageMaxSearchDepth = average(averageMaxSearchDepth, i, 
				      csp.getMaxSearchDepth());
	i++; 
      }
      else {
#ifdef USEAC3kR
	output(results, 
	       fileName, cacheSize, method, 
	       clock1, 
	       csp.cchecks(), csp.dchecks(), csp.ochecks(),
	       ncip, nnip);
#else
	output(results, 
	       fileName, ALGORITHM, 
	       clock1, 
	       csp.cchecks(), csp.dchecks(), csp.ochecks(),
	       ncip, nnip);

#endif
	ncip = nnip = 0; // reset the counter for every instance problem
      }
      //cout << "Finished with Instance " << i << endl;
      //getchar();
      // time(&et);
      // total_checks += num_checks;
      // printf("timing from %ld to %ld\n", st, et);

    } // end of for loop from start to end

    if(averagePerformance)
#ifdef USEAC3kR
      output(results,
	     f1+"?"+f2, cacheSize, method,
	     clock,
	     averageCChecks, averageDChecks, averageOChecks,
	     ncip, nnip);
#else
      output(results,
	     f1+"?"+f2, ALGORITHM,
	     clock,
	     averageCChecks, averageDChecks, averageOChecks,
	     ncip, nnip);
#endif
    
    std::cout << "average cchecks+dchecks+ochecks: average backtracks: average search depth\n";
    std::cout << averageCChecks << '+' << averageDChecks
	      << '+' << averageOChecks
	    << ':' << averageBacktracks << ':' 
	    << averageMaxSearchDepth << '\n'; 
    printf("the consistent instances are (%d) \n", ncip);
    printf("the inconsistent instances are (%d) \n", nnip);
    std::cout << "Total time:" << clock << '\n';
  } // processing next line of configure.conf 
}

double average(double averageValue, int numOfValues, double newValue){
  double temp;
  
  temp = (double)averageValue/(numOfValues+1)*numOfValues + 
    (double) newValue/(numOfValues + 1);
  return temp; 
}
